using System;
using System.IO;
using GameFramework;
using GameFramework.Resource;
using ILRuntime.CLR.TypeSystem;
using UnityEngine;
using UnityGameFramework.Runtime;
using AppDomain = ILRuntime.Runtime.Enviorment.AppDomain;
using ILRuntime;
using ILRuntime.CLR.Method;

namespace StarForce
{
    /// <summary>
    /// ILRuntime组件。
    /// </summary>
    public sealed partial class ILRuntimeComponent : GameFrameworkComponent
    {
        /// <summary>
        /// 热更新类型。
        /// </summary>
        public enum HotfixTpye
        {
            /// <summary>
            /// DLL文件。
            /// </summary>
            DLL,

            /// <summary>
            /// PDB文件。
            /// </summary>
            PDB
        }

        /// <summary>
        /// 是否使用热更模式
        /// </summary>
        [SerializeField] private bool m_ILRuntimeMode = true;

        public bool ILRuntimeMode
        {
            get { return m_ILRuntimeMode; }
        }

        private bool m_HotfixLoaded = false;

        private AppDomain m_AppDomain = null;

        private LoadAssetCallbacks m_LoadAssetCallbacks;

        private MemoryStream m_FileStream;

        private MemoryStream m_PDBStream;

        private bool m_DLLLoaded = false;

        private byte[] m_DLLBytes;

        private bool m_PDBLoaded = false;

        private byte[] m_PDBBytes;

        private IMethod m_Update;

        private IMethod m_ShutDown;

        /// <summary>
        /// ILRuntime入口对象。
        /// </summary>
        public AppDomain AppDomain
        {
            get { return m_AppDomain; }
        }

        public bool HotfixLoaded
        {
            get { return m_HotfixLoaded; }
        }

        private void Update()
        {
            if (m_Update != null)
            {
                m_AppDomain.Invoke(m_Update, null, Time.deltaTime, Time.unscaledDeltaTime);
            }
        }

        private void OnDestroy()
        {
            if (m_ShutDown != null)
            {
                Log.Error("fuck");
                m_AppDomain.Invoke(m_ShutDown, null);
            }
        }

        public void LoadHotfixDLL()
        {
            m_HotfixLoaded = false;

            m_AppDomain = new AppDomain();
            ILRuntimeHelper.InitILRuntime(m_AppDomain);

            //启动调试服务器
            AppDomain.DebugService.StartDebugService(56000);
            Log.Info("启动了ILRuntime调试服务器");

            m_LoadAssetCallbacks = new LoadAssetCallbacks(OnLoadHotfixDLLSuccess, OnLoadHotfixDLLFailure);

            GameEntry.Resource.LoadAsset(
                AssetUtility.GetHotfixDLLAsset(Utility.Text.Format("{0}.Hotfix.dll", Constant.AssemblyInfo.Namespace)),
                typeof(TextAsset),
                m_LoadAssetCallbacks, HotfixTpye.DLL);
            GameEntry.Resource.LoadAsset(
                AssetUtility.GetHotfixDLLAsset(Utility.Text.Format("{0}.Hotfix.pdb", Constant.AssemblyInfo.Namespace)),
                typeof(TextAsset),
                m_LoadAssetCallbacks, HotfixTpye.PDB);
        }

        private void OnLoadHotfixDLLSuccess(string assetname, object asset, float duration, object userdata)
        {
            HotfixTpye hotfixTpye = (HotfixTpye) userdata;
            TextAsset textAsset = (TextAsset) asset;
            byte[] bytes = textAsset.bytes;
            int length = bytes.Length;
            switch (hotfixTpye)
            {
                case HotfixTpye.DLL:
                    m_DLLBytes = new byte[length];
                    Array.Copy(bytes, m_DLLBytes, length);
                    m_DLLLoaded = true;
                    break;
                case HotfixTpye.PDB:
                    m_PDBBytes = new byte[length];
                    Array.Copy(bytes, m_PDBBytes, length);
                    m_PDBLoaded = true;
                    break;
            }

            if (m_DLLLoaded && m_PDBLoaded)
            {
                m_HotfixLoaded = true;
                m_DLLLoaded = false;
                m_PDBLoaded = false;

                MemoryStream fs = new MemoryStream(m_DLLBytes);
                MemoryStream p = new MemoryStream(m_PDBBytes);

                AppDomain.LoadAssembly(fs, p, new Mono.Cecil.Pdb.PdbReaderProvider());
            }
            Log.Info("加载dll成功");

            GameEntry.Resource.UnloadAsset(asset);
        }

        private void OnLoadHotfixDLLFailure(string assetname, LoadResourceStatus status, string errormessage,
            object userdata)
        {
            HotfixTpye hotfixTpye = (HotfixTpye) userdata;
            switch (hotfixTpye)
            {
                case HotfixTpye.DLL:
                    Log.Error(errormessage);
                    break;
                case HotfixTpye.PDB:
                    Log.Error(errormessage);
                    break;
            }
        }

        public void HotfixStart()
        {
            m_HotfixLoaded = false;

            string typeFullName = Utility.Text.Format("{0}.Hotfix.HotfixGameEntry", Constant.AssemblyInfo.Namespace);
            IType type = m_AppDomain.LoadedTypes[typeFullName];

            m_AppDomain.Invoke(typeFullName, "Start", null, null);

            m_Update = type.GetMethod("Update", 2);

            m_ShutDown = type.GetMethod("ShutDown", 0);
        }
    }
}